package storage

import (
	"strconv"
)

type Number interface {
	float32 | float64 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | string
}

type (
	// Modify 修改器
	Modify interface{}

	// Field2Modify 获取字段修改器
	Field2Modify interface {
		GetFieldModify(field string) Modify
	}
	// BaseModify 基础修改器 字段值覆盖
	BaseModify interface {
		Set(val interface{})
	}
	// MapModify map类型修改器
	MapModify interface {
		BaseModify
		// Unset 删除一个key
		Unset(key string)
		// Update 以key为单位修改 key不存在增加，存在覆盖
		Update(val map[string]interface{})
	}
	// ListModify 切片修改器
	ListModify interface {
		BaseModify
		// Pop 移除指定索引的值
		Pop(index int64)
		// Slice 截取指定个数的元素 正数从前往后截取，复数相反
		Slice(number int64)
		// Extend 向切片后面追加任意个数元素
		Extend(vals ...interface{})
		// Replace 将指定索引位置的元素替换给定元素
		Replace(index int, val interface{})
	}
	// NumberModify 数字修改器
	NumberModify interface {
		BaseModify
		// Inc 累加
		Inc(val interface{})
	}
)

type BaseModifyImpl[V any] struct {
	ValPtr *V
}

func NewBaseModify[V any](ptr *V) BaseModify {
	return &BaseModifyImpl[V]{ValPtr: ptr}
}

func (i *BaseModifyImpl[V]) Set(val interface{}) {
	*(i.ValPtr) = val.(V)
}

type NumberModifyImpl[V Number] struct {
	BaseModifyImpl[V]
}

func NewNumberModify[V Number](ptr *V) NumberModify {
	return &NumberModifyImpl[V]{
		BaseModifyImpl: BaseModifyImpl[V]{
			ValPtr: ptr,
		},
	}
}

func (i *NumberModifyImpl[V]) Inc(val interface{}) {
	*(i.ValPtr) += val.(V)
}

type MapModifyImpl[V any] struct {
	BaseModifyImpl[map[string]V]
}

func NewMapModify[V any](ptr *map[string]V) MapModify {
	return &MapModifyImpl[V]{
		BaseModifyImpl: BaseModifyImpl[map[string]V]{
			ValPtr: ptr,
		},
	}
}

func (m *MapModifyImpl[V]) Update(val map[string]interface{}) {
	for k, v := range val {
		(*m.ValPtr)[k] = v.(V)
	}
}

func (m *MapModifyImpl[V]) Unset(key string) {
	delete(*m.ValPtr, key)
}

type ListModifyImpl[V any] struct {
	BaseModifyImpl[[]V]
}

func NewListModify[V any](ptr *[]V) ListModify {
	return &ListModifyImpl[V]{
		BaseModifyImpl: BaseModifyImpl[[]V]{
			ValPtr: ptr,
		},
	}
}

func (l *ListModifyImpl[V]) Pop(index int64) {
	*l.ValPtr = append((*l.ValPtr)[:index], (*l.ValPtr)[index:]...)
	return
}

func (l *ListModifyImpl[V]) Extend(values ...interface{}) {
	externals := make([]V, len(values))
	for i := range values {
		externals[i] = values[i].(V)
	}
	*l.ValPtr = append(*l.ValPtr, externals...)
}

func (l *ListModifyImpl[V]) Slice(number int64) {
	if number > 0 {
		if int(number) >= len(*l.ValPtr) {
			number = int64(len(*l.ValPtr))
		}
		*l.ValPtr = (*l.ValPtr)[:number]
	} else {
		number *= -1
		if int(number) >= len(*l.ValPtr) {
			return
		}
		*l.ValPtr = (*l.ValPtr)[int64(len(*l.ValPtr))-number:]
	}
}

func (l *ListModifyImpl[V]) Replace(index int, val interface{}) {
	(*l.ValPtr)[index] = val.(V)
}

type ModifyScript map[string]any

func (i *Item) Update(update ModifyScript) {
	for field, option := range update {
		switch option.(type) {
		case map[string]interface{}:
			for act, args := range option.(map[string]interface{}) {
				switch act {
				case "$set":
					i.Value().(Field2Modify).GetFieldModify(field).(BaseModify).Set(args)
				case "$inc":
					i.Value().(Field2Modify).GetFieldModify(field).(NumberModify).Inc(args)
				case "$unset":
					modify := i.Value().(Field2Modify).GetFieldModify(field).(MapModify)
					for unk := range args.(map[string]interface{}) {
						modify.Unset(unk)
					}
				case "$update":
					i.Value().(Field2Modify).GetFieldModify(field).(MapModify).Update(args.(map[string]interface{}))
				case "$pop":
					i.Value().(Field2Modify).GetFieldModify(field).(ListModify).Pop(args.(int64))
				case "$extend":
					i.Value().(Field2Modify).GetFieldModify(field).(ListModify).Extend(args.([]interface{})...)
				case "$replace":
					for idx, val := range args.(map[string]interface{}) {
						if index, err := strconv.Atoi(idx); err != nil {
							panic(err)
						} else {
							i.Value().(Field2Modify).GetFieldModify(field).(ListModify).Replace(index, val)
						}
					}
				case "$slice":
					i.Value().(Field2Modify).GetFieldModify(field).(ListModify).Slice(args.(int64))
				}
			}
		default:
			i.Value().(Field2Modify).GetFieldModify(field).(BaseModify).Set(option)
		}
	}
}
